#pragma once

#include <vector>

namespace gtl
{

namespace funs
{

union fun_buf
{
	mutable void* obj_ptr;
	mutable void (*fun_ptr)();
	mutable char data;
};

struct fun_ptr_tag { };
struct obj_ptr_tag { };

template<class Functor>
struct vtable_base
{
	static void* manager(void* obj_ptr)
	{
		return new Functor(*static_cast<Functor*>(obj_ptr));
	}
};

class fun_base
{
protected:
	fun_base()
	{
		m_vtable = 0;
		m_functor.fun_ptr = 0;
		m_need_destory_obj_ptr = false;
	}

	~fun_base()
	{
		if(m_need_destory_obj_ptr)
			delete m_functor.obj_ptr;
	}

private:
	struct dummy 
	{
		void nonnull() {};
	};

	typedef void (dummy::*safe_bool)();

public:
	bool empty() const
	{
		return !m_functor.fun_ptr;
	}

	void clear()
	{
		if(m_need_destory_obj_ptr)
			delete m_functor.obj_ptr;

		m_functor.fun_ptr = NULL;
	}

public:
	operator safe_bool() const
	{
		return empty() ? 0 : &dummy::nonnull; 
	}

	bool operator!() const
	{
		return this->empty(); 
	}

protected:
	fun_base& operator=(const fun_base& f)
	{
		clear();

		if(m_vtable != 0)
			m_functor.obj_ptr = m_vtable(f.m_functor.obj_ptr);
		else
			m_functor.fun_ptr = f.m_functor.fun_ptr;

		return *this;
	}

	template<class Function>
	void assign_to(Function function, fun_ptr_tag)
	{
		clear();

		m_vtable = 0;
		m_functor.fun_ptr = reinterpret_cast<void(*)()>(function);
		m_need_destory_obj_ptr = false;
	}

	template<class Functor>
	void assign_to(const Functor& functor, obj_ptr_tag)
	{
		clear();

		m_vtable = &vtable_base<Functor>::manager;
		m_functor.obj_ptr = new Functor(functor);
		m_need_destory_obj_ptr = true;
	}

protected:
	typedef void* (*vtable_type)(void*);
	fun_buf		m_functor;
	vtable_type	m_vtable;
	bool		m_need_destory_obj_ptr;
};

template<class R>
struct fun_return_value
{
	static R result()
	{
		return R();
	}
};

template<>
struct fun_return_value<void>
{
	static void result()
	{
		return;
	}
};

template<class FunctionPtr, class R>
struct fun_invoker0
{
	static R invoke(fun_buf fun_ptr)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))();
	}
};

template<class FunctionPtr, class R, class T1>
struct fun_invoker1
{
	static R invoke(fun_buf fun_ptr, T1 t1)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))(t1);
	}
};

template<class FunctionPtr, class R, class T1, class T2>
struct fun_invoker2
{
	static R invoke(fun_buf fun_ptr, T1 t1, T2 t2)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))(t1, t2);
	}
};

template<class FunctionPtr, class R, class T1, class T2, class T3>
struct fun_invoker3
{
	static R invoke(fun_buf fun_ptr, T1 t1, T2 t2, T3 t3)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))(t1, t2, t3);
	}
};

template<class FunctionPtr, class R, class T1, class T2, class T3, class T4>
struct fun_invoker4
{
	static R invoke(fun_buf fun_ptr, T1 t1, T2 t2, T3 t3, T4 t4)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))(t1, t2, t3, t4);
	}
};

template<class FunctionPtr, class R, class T1, class T2, class T3, class T4, class T5>
struct fun_invoker5
{
	static R invoke(fun_buf fun_ptr, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
	{
		if(fun_ptr.fun_ptr == 0)
			return fun_return_value<R>::result();
		return (*reinterpret_cast<FunctionPtr>(fun_ptr.obj_ptr))(t1, t2, t3, t4, t5);
	}
};

} // end of namespace funs

template<class Fun>
class fun;

template<class R>
class fun<R()> : public funs::fun_base
{
public:
	typedef R(fun_type)();
	typedef R(*invoker_type)(funs::fun_buf);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker0<fun_type*, R> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker0<Functor*, R> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()()
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor);
	}

protected:
	invoker_type m_invoker;
};

template<class R, class T1>
class fun<R(T1)> : public funs::fun_base
{
public:
	typedef R(fun_type)(T1);
	typedef R(*invoker_type)(funs::fun_buf, T1);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker1<fun_type*, R, T1> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker1<Functor*, R, T1> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()(T1 t1)
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor, t1);
	}

protected:
	invoker_type m_invoker;
};

template<class R, class T1, class T2>
class fun<R(T1, T2)> : public funs::fun_base
{
public:
	typedef R(fun_type)(T1, T2);
	typedef R(*invoker_type)(funs::fun_buf, T1, T2);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker2<fun_type*, R, T1, T2> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker2<Functor*, R, T1, T2> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()(T1 t1, T2 t2)
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor, t1, t2);
	}

protected:
	invoker_type m_invoker;
};

template<class R, class T1, class T2, class T3>
class fun<R(T1, T2, T3)> : public funs::fun_base
{
public:
	typedef R(fun_type)(T1, T2, T3);
	typedef R(*invoker_type)(funs::fun_buf, T1, T2, T3);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker3<fun_type*, R, T1, T2, T3> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker3<Functor*, R, T1, T2, T3> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()(T1 t1, T2 t2, T3 t3)
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor, t1, t2, t3);
	}

protected:
	invoker_type m_invoker;
};

template<class R, class T1, class T2, class T3, class T4>
class fun<R(T1, T2, T3, T4)> : public funs::fun_base
{
public:
	typedef R(fun_type)(T1, T2, T3, T4);
	typedef R(*invoker_type)(funs::fun_buf, T1, T2, T3, T4);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker4<fun_type*, R, T1, T2, T3, T4> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker4<Functor*, R, T1, T2, T3, T4> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()(T1 t1, T2 t2, T3 t3, T4 t4)
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor, t1, t2, t3, t4);
	}

protected:
	invoker_type m_invoker;
};

template<class R, class T1, class T2, class T3, class T4, class T5>
class fun<R(T1, T2, T3, T4, T5)> : public funs::fun_base
{
public:
	typedef R(fun_type)(T1, T2, T3, T4, T5);
	typedef R(*invoker_type)(funs::fun_buf, T1, T2, T3, T4, T5);

public:
	fun()
	{
		m_invoker = 0;
	}

	fun(fun_type fun_ptr)
	{
		*this = fun_ptr;
	}

	template<class Functor>
	fun(Functor obj)
	{
		*this = obj;
	}

	fun(const fun& f)
	{
		*this = f;
	}

public:
	inline void operator=(fun_type fun_ptr)
	{
		typedef funs::fun_invoker5<fun_type*, R, T1, T2, T3, T4, T5> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(fun_ptr, funs::fun_ptr_tag());
	}

	template<class Functor>
	inline void operator=(Functor obj)
	{
		typedef funs::fun_invoker5<Functor*, R, T1, T2, T3, T4, T5> invoker_type;

		m_invoker = &invoker_type::invoke;
		assign_to(obj, funs::obj_ptr_tag());
	}

	inline fun& operator=(const fun& f)
	{
		fun_base::operator=(f);
		m_invoker = f.m_invoker;
		return *this;
	}

public:
	inline R operator()(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
	{
		if(m_invoker == NULL)
			return funs::fun_return_value<R>::result();
		return m_invoker(m_functor, t1, t2, t3, t4, t5);
	}

protected:
	invoker_type m_invoker;
};

} // end of namespace gtl